#pragma once
#include <vector>
#include <Part/App/Geometry.h>
namespace Part {
    class GeomLineSegment;
    class GeomArcOfCircle;
    class GeomBSplineCurve;
    class GeomEllipse;
    class GeomCircle;
}

namespace Base {
    class Vector2d;
}

namespace SketcherGui {
    Part::GeomPoint* create_point(double x, double y);
    Part::GeomLineSegment* create_segment(const Base::Vector2d& v1, const Base::Vector2d& v2) ;
    const Part::GeomArcOfCircle* createArcOfCircle(const Base::Vector2d& center, double radius, double start_angle, double end_angle);
    const Part::GeomCircle* create_circle(const Base::Vector2d& center, double radius);
    Part::GeomEllipse* create_elliopse(const Base::Vector2d& v1, const Base::Vector2d& v2, const Base::Vector2d& v3);
    const Part::GeomBSplineCurve*  create_bspline(const std::vector<Base::Vector2d>& list, bool cond, int spline_degree);
    Part::GeomArcOfEllipse* createArcOfEllipse(const Base::Vector2d& vector2d, const Base::Vector2d& min_axis_point, const Base::Vector2d& center_point, double start_angle, double end_angle);
    Part::GeomArcOfHyperbola* createArcOfHyperbola(const Base::Vector2d& vector2d, const Base::Vector2d& min_axis_point, const Base::Vector2d& center_point, double start_angle, double end_angle);
    Part::GeomArcOfParabola* createArcOfParabola(const Base::Vector2d& vector2d, const Base::Vector2d& axis_point, double start_angle, double end_angle);
    
    template<typename...Args>
    Sketcher::Constraint*  create_constraint(const Sketcher::ConstraintType& ct, Args&&... args) {
        int  FirstIndex = Sketcher::GeoEnum::GeoUndef;
        int  FirstPos   = static_cast<int>(Sketcher::PointPos::none);
        int  SecondIndex= Sketcher::GeoEnum::GeoUndef;
        int  SecondPos  = static_cast<int>(Sketcher::PointPos::none);
        int  ThirdIndex = Sketcher::GeoEnum::GeoUndef;
        int  ThirdPos   = static_cast<int>(Sketcher::PointPos::none);
        double Value    = 0;
        int intArg1, intArg2, intArg3, intArg4, intArg5;
        Sketcher::Constraint* constraint = new Sketcher::Constraint();
        constraint->Type = ct;
        constexpr std::size_t numArgs = sizeof...(Args);
        std::vector<double> results = {static_cast<double>(args)...};
        
        FirstIndex= static_cast<int>(results[0]);
        switch (ct) {
        case Sketcher::ConstraintType::Horizontal:
        case Sketcher::ConstraintType::Vertical:
        case Sketcher::ConstraintType::Block:{
            if(numArgs==1) {
                constraint->First = FirstIndex;
            }
            if(numArgs==4) {
                constraint->First     = static_cast<int>(results[0]);
                constraint->FirstPos  = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->Second    = static_cast<int>(results[2]);
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);
            }
            break;
        }
        case Sketcher::ConstraintType::Tangent:
        case Sketcher::ConstraintType::Parallel:
        case Sketcher::ConstraintType::Perpendicular:
        case Sketcher::ConstraintType::Equal:
        case Sketcher::ConstraintType::InternalAlignment:
        {
            if(numArgs == 2) {
                FirstIndex= static_cast<int>(results[0]);
                SecondIndex= static_cast<int>(results[1]);
                constraint->First = FirstIndex;
                constraint->Second = SecondIndex;
            }
            if(numArgs == 3) {
                constraint->First    = static_cast<int>(results[0]);
                constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->Second   = static_cast<int>(results[2]);
            }
            if(numArgs == 4) {
                constraint->First    = static_cast<int>(results[0]);
                constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->Second   = static_cast<int>(results[2]);
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);
            }
            break;
        }
        case Sketcher::ConstraintType::Distance: 
        case Sketcher::ConstraintType::Angle:
        case Sketcher::ConstraintType::DistanceX:
        case Sketcher::ConstraintType::DistanceY:
        case Sketcher::ConstraintType::Radius:
        case Sketcher::ConstraintType::Diameter:
        case Sketcher::ConstraintType::Weight: {
            if(numArgs == 2) {
                constraint->First = static_cast<int>(results[0]);
                constraint->setValue(Value);
            }
            if(numArgs== 3) {
                constraint->First    = static_cast<int>(results[0]);
                constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->setValue(Value);
            }
            if(numArgs== 4) {
                constraint->First    = static_cast<int>(results[0]);
                constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->Second   = static_cast<int>(results[2]);
                constraint->setValue(Value);
            }
            if(numArgs== 5) {
                constraint->First    = static_cast<int>(results[0]);
                constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
                constraint->Second   = static_cast<int>(results[2]);
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);
                constraint->setValue(Value);
            }
            break;
        }
        case Sketcher::ConstraintType::PointOnObject: {
            constraint->First    = static_cast<int>(results[0]);
            constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
            constraint->Second   = static_cast<int>(results[2]);;
            break;
        }
        case Sketcher::Coincident: {
            constraint->First    = static_cast<int>(results[0]);
            constraint->FirstPos = (Sketcher::PointPos) static_cast<int>(results[1]);
            constraint->Second   = static_cast<int>(results[2]);
        }
        case Sketcher::Symmetric:
            if(numArgs == 5) {
                constraint->First     = static_cast<int>(results[0]);
                constraint->FirstPos  = (Sketcher::PointPos) static_cast<int>(results[1]);;
                constraint->Second    = static_cast<int>(results[2]);;
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);;
                constraint->Third     = static_cast<int>(results[4]);;
            }
            if(numArgs == 6) {
                constraint->First     = static_cast<int>(results[0]);
                constraint->FirstPos  = (Sketcher::PointPos) static_cast<int>(results[1]);;
                constraint->Second    = static_cast<int>(results[2]);;
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);;
                constraint->Third     = static_cast<int>(results[4]);;
                constraint->ThirdPos  = (Sketcher::PointPos) static_cast<int>(results[5]);;
            }
            break;
        case Sketcher::SnellsLaw:
            if(numArgs == 6) {
                constraint->First     = static_cast<int>(results[0]);;
                constraint->FirstPos  = (Sketcher::PointPos) static_cast<int>(results[1]);;
                constraint->Second    = static_cast<int>(results[2]);;
                constraint->SecondPos = (Sketcher::PointPos) static_cast<int>(results[3]);;
                constraint->Third     = static_cast<int>(results[4]);;
                constraint->ThirdPos  = Sketcher::PointPos::none;
                constraint->setValue(results[5]);
            }
        default:
            Base::Console().Error("create_constraint: Unknown constraint type");
            break;
        }
        return constraint;
    }
    Sketcher::Constraint * create_TangentViaPoint(int d1,int d2,int d3,int d4);
    Sketcher::Constraint * create_PerpendicularViaPoint(int d1,int d2,int d3,int d4);
    Sketcher::Constraint * create_AngleViaPoint(int d1,int d2,int d3,int d4,double Value);
}
